package hu.rsc.forbiddenwords.server;

import hu.rsc.forbiddenwords.client.ForbiddenWordsService;
import hu.rsc.forbiddenwords.server.dictionary.DictionaryWorker;
import hu.rsc.forbiddenwords.server.persistent.Application;
import hu.rsc.forbiddenwords.server.persistent.Entry;
import hu.rsc.forbiddenwords.server.persistent.Language;
import hu.rsc.forbiddenwords.server.persistent.PMF;
import hu.rsc.forbiddenwords.shared.ApplicationDTO;
import hu.rsc.forbiddenwords.shared.ColumnFilterInfo;
import hu.rsc.forbiddenwords.shared.ColumnFilterList;
import hu.rsc.forbiddenwords.shared.EntryDTO;
import hu.rsc.forbiddenwords.shared.FieldMapping;
import hu.rsc.forbiddenwords.shared.LanguageDTO;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.gen2.table.client.TableModelHelper;
import com.google.gwt.gen2.table.client.TableModelHelper.Request;
import com.google.gwt.gen2.table.client.TableModelHelper.Response;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class ForbiddenWordsServiceImpl extends RemoteServiceServlet implements ForbiddenWordsService {

    private static final Long NEW_ID = 0l;

    public static final Long DEFAULT_ID = 1l;

    public static final String DEFAULT_USERNAME = "Red Staff Corporation";

    public static final String DEFAULT_APPLICATION_NAME = "Base";

    public static final String DEAFULT_EMAIL = "jon.arbuckle1@gmail.com";

    @Override
    public String filter(String input) throws IllegalArgumentException {
	DictionaryWorker d = new DictionaryWorker(null);
	String result = d.filter(input);
	d.flushResult();
	return result;
    }

    @Override
    public int getApplicationCount(ColumnFilterList filters) {
	return getApplications(FieldMapping.APP_ID, true, 0, Integer.MAX_VALUE).size();
    }

    @Override
    public Response<ApplicationDTO> getApplications(Request request) {
	int startRow = request.getStartRow();
	int numRows = request.getNumRows();

	return new TableModelHelper.SerializableResponse<ApplicationDTO>(getApplications(request.getColumnSortList().getPrimaryColumn(), request
		.getColumnSortList().getPrimaryColumnSortInfo().isAscending(), startRow, numRows));
    }

    @Override
    public Response<EntryDTO> getEntries(Long application, Request request, ColumnFilterList filters) {
	int startRow = request.getStartRow();
	int numRows = request.getNumRows();
	return new TableModelHelper.SerializableResponse<EntryDTO>(getEntries(application, request.getColumnSortList().getPrimaryColumn(), request
		.getColumnSortList().getPrimaryColumnSortInfo().isAscending(), startRow, numRows, filters));
    }

    @Override
    public int getEntriesCount(Long application, ColumnFilterList filters) {
	return getEntries(application, FieldMapping.ENTRY_ID, true, 0, Integer.MAX_VALUE, filters).size();
    }

    @Override
    public int getLanguageCount(ColumnFilterList filters) {
	return getLanguages(FieldMapping.LANG_ID, true, 0, Integer.MAX_VALUE).size();
    }

    @Override
    public List<LanguageDTO> getLanguages() {
	return getLanguages(FieldMapping.LANG_NAME, true, 0, Integer.MAX_VALUE);
    }

    @Override
    public Response<LanguageDTO> getLanguages(Request request) {
	int startRow = request.getStartRow();
	int numRows = request.getNumRows();

	return new TableModelHelper.SerializableResponse<LanguageDTO>(getLanguages(request.getColumnSortList().getPrimaryColumn(), request.getColumnSortList()
		.getPrimaryColumnSortInfo().isAscending(), startRow, numRows));
    }

    @Override
    public ApplicationDTO saveApplication(ApplicationDTO app) {
	PersistenceManager pm = PMF.get().getPersistenceManager();

	Application e = null;
	if (!app.getId().equals(NEW_ID)) {
	    e = pm.getObjectById(Application.class, app.getId());
	    if (!e.getUserinfo().equals(getUserId())) {
		return null;
	    }

	} else {
	    e = new Application(null, app.getName(), app.getLanguage());
	}

	if (e.getUserinfo() == null) {
	    UserService userService = UserServiceFactory.getUserService();
	    e.setUserinfo(userService.getCurrentUser().getUserId());
	}

	e.setName(app.getName());
	e.setLanguage(app.getLanguage());
	e.setOpened(app.isOpened());

	try {
	    pm.makePersistent(e);
	} finally {
	    pm.close();
	}
	app.setId(e.getKey().getId());
	return app;
    }

    @Override
    public EntryDTO saveEntry(EntryDTO entry) {
	PersistenceManager pm = PMF.get().getPersistenceManager();

	Entry e = null;
	if (!entry.getId().equals(NEW_ID)) {
	    e = pm.getObjectById(Entry.class, entry.getId());

	} else {
	    e = new Entry(null, entry.getWord(), entry.getCount());
	}

	e.setWord(entry.getWord());
	Application a = null;
	if (entry.getApplication() != null && !entry.getApplication().equals(NEW_ID)) {
	    a = pm.getObjectById(Application.class, entry.getApplication());
	    if (!a.getUserinfo().equals(getUserId())) {
		return null;
	    }
	}
	e.setApplication(a != null ? a.getKey() : null);

	try {
	    pm.makePersistent(e);
	} catch (Exception ex) {
	    ex.printStackTrace();
	} finally {
	    pm.close();
	}
	entry.setId(e.getKey().getId());
	return entry;
    }

    @Override
    public LanguageDTO saveLanguage(LanguageDTO lang) {
	PersistenceManager pm = PMF.get().getPersistenceManager();

	Language e = null;
	if (!lang.getId().equals(NEW_ID)) {
	    e = pm.getObjectById(Language.class, lang.getId());

	} else {
	    e = new Language(lang.getName(), lang.getCode());
	}

	e.setName(lang.getName());
	e.setCode(lang.getCode().toLowerCase());

	try {
	    pm.makePersistent(e);
	} finally {
	    pm.close();
	}
	lang.setId(e.getKey().getId());
	return lang;
    }

    /**
     * Escape an html string. Escaping data received from the client helps to prevent cross-site script vulnerabilities.
     * 
     * @param html
     *            the html string to escape
     * @return the escaped string
     */
    private String escapeHtml(String html) {
	if (html == null) {
	    return null;
	}
	return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
    }

    private List<ApplicationDTO> getApplications(int sorting, boolean asc, int startRow, int numRows) {
	//initDatabase();
	List<ApplicationDTO> dtos = new ArrayList<ApplicationDTO>();
	PersistenceManager pm = PMF.get().getPersistenceManager();

	Query query = pm.newQuery(Application.class);
	query.setFilter("userinfo == userinfoParam || opened");
	query.declareParameters("String userinfoParam");
	switch (sorting) {
	case FieldMapping.APP_NAME: {
	    query.setOrdering("name " + (asc ? "asc" : "desc"));
	    break;
	}
	case FieldMapping.APP_LANG: {
	    query.setOrdering("language " + (asc ? "asc" : "desc"));
	    break;
	}
	case FieldMapping.APP_OPENED: {
	    query.setOrdering("opened " + (asc ? "asc" : "desc"));
	    break;
	}
	}
	String userinfo = getUserId();
	try {
	    List<Application> results = (List<Application>) query.execute(userinfo);
	    if (!results.isEmpty()) {
		for (Application e : results) {
		    dtos.add(DtoFactory.getApplicationDTO(e, userinfo));
		}
	    }
	} finally {
	    query.closeAll();
	    pm.close();
	}
	return dtos;
    }

    private List<EntryDTO> getEntries(Long application, int sorting, boolean asc, int startRow, int numRows, ColumnFilterList filters) {
	List<EntryDTO> dtos = new ArrayList<EntryDTO>();
	PersistenceManager pm = PMF.get().getPersistenceManager();

	Query query = pm.newQuery(Entry.class);
	switch (sorting) {
	case FieldMapping.ENTRY_WORD: {
	    query.setOrdering("word " + (asc ? "asc" : "desc"));
	    break;
	}
	case FieldMapping.ENTRY_COUNT: {
	    query.setOrdering("count " + (asc ? "asc" : "desc"));
	    break;
	}
	}

	List params = new ArrayList();
	if (!application.equals(NEW_ID)) {
	    query.setFilter("application == applicationId");
	    //query.declareParameters("Application applicationId");
	    query.declareParameters(Key.class.getName() + " applicationId");
	}

	Application a = null;
	try {
	    a = pm.getObjectById(Application.class, application);
	    params.add(a.getKey());
	} catch (Exception e) {
	    return dtos;
	}

	if (filters != null) {
	    Iterator<ColumnFilterInfo> iterator = filters.iterator();
	    while (iterator.hasNext()) {
		ColumnFilterInfo info = iterator.next();
		if (info.getColumn() == FieldMapping.ENTRY_WORD) {
		    query.setFilter("word == wordParam");
		    query.declareParameters("String wordParam");
		    params.add(info.getFilter());
		}
	    }
	}

	try {
	    List<Entry> results = (List<Entry>) query.execute(params);

	    if (!results.isEmpty()) {
		for (Entry e : results) {
		    dtos.add(DtoFactory.getEntryDTO(e));
		}
	    }
	} finally {
	    query.closeAll();
	    pm.close();
	}
	return dtos;
    }

    private List<LanguageDTO> getLanguages(int sorting, boolean asc, int startRow, int numRows) {

	List<LanguageDTO> dtos = new ArrayList<LanguageDTO>();
	PersistenceManager pm = PMF.get().getPersistenceManager();

	Query query = pm.newQuery(Language.class);
	switch (sorting) {
	case FieldMapping.LANG_NAME: {
	    query.setOrdering("name " + (asc ? "asc" : "desc"));
	    break;
	}
	case FieldMapping.LANG_CODE: {
	    query.setOrdering("code " + (asc ? "asc" : "desc"));
	    break;
	}
	}

	try {
	    List<Language> results = (List<Language>) query.execute();
	    if (!results.isEmpty()) {
		for (Language e : results) {
		    dtos.add(DtoFactory.getLanguageDTO(e));
		}
	    }
	} finally {
	    query.closeAll();
	    pm.close();
	}
	return dtos;
    }

    private String getUserId() {
	UserService userService = UserServiceFactory.getUserService();
	User user = userService.getCurrentUser();
	if (user == null) {
	    return null;
	}
	return user.getUserId();
    }

}
